package com.yunqian.util.wechat;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunqian.exception.ActionException;
import com.yunqian.util.HttpUtil;
import com.yunqian.util.validate.Validator;
import com.yunqian.util.wechat.bean.WxMpTemplateData;
import com.yunqian.util.wechat.bean.WxMpTemplateMessage;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zou
 * @describe 微信接口工具类
 * @date 2018/7/17
 */
@Log4j2
public class WechatUtil {

    /**
     * 小程序或公众号获取access_token
     * 参考：https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183
     *
     * @param appId
     * @param appSecret
     * @return
     */
    public static JSONObject getAccessToken(String appId, String appSecret) {
        String WX_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        String requestUrl = WX_URL.replace("APPID", appId).replace("APPSECRET", appSecret);
        String returnValue = HttpUtil.GET(requestUrl);
        // 将得到的字符串转换为json
        JSONObject convertValue = (JSONObject) JSON.parse(returnValue);
        String accessToken = (String) convertValue.get("access_token");
        if (StringUtils.isBlank(accessToken)) {
            throw new ActionException("获取accessToken出错，errcode = " + convertValue.get("errcode"));
        }
        return convertValue;
    }

    /**
     * 公众号网页授权access_token
     * 参考：https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
     *
     * @param appId  公众号的唯一标识
     * @param secret 公众号的appsecret
     * @param code
     * @return access_token/expires_in/refresh_token/openid/scope
     */
    public static JSONObject getOauth2AccessToken(String appId, String secret, String code) {
        String WX_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        String requestUrl = WX_URL.replace("APPID", appId).replace("SECRET", secret).replace("CODE", code);
        String returnValue = HttpUtil.GET(requestUrl);
        // 将得到的字符串转换为json
        JSONObject convertValue = (JSONObject) JSON.parse(returnValue);
        Integer errcode = (Integer) convertValue.get("errcode");
        if (Validator.isNotNull(errcode)) {
            throw new ActionException("获取accessToken出错，errcode = " + errcode + ",errmsg = " + convertValue.get("errmsg"));
        }
        return convertValue;
    }

    /**
     * 公众号获取授权用户信息
     * 参考：https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
     *
     * @param accessToken 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
     * @param openId      用户的唯一标识
     * @return openid/nickname/sex/province/city/country/headimgurl/privilege/unionid
     */
    public static JSONObject getUserInfo(String accessToken, String openId) {
        String WX_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
        String requestUrl = WX_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
        String returnValue = HttpUtil.GET(requestUrl);
        JSONObject convertValue = (JSONObject) JSON.parse(returnValue);
        Integer errcode = (Integer) convertValue.get("errcode");
        if (Validator.isNotNull(errcode)) {
            throw new ActionException("获取用户信息出错，errcode = " + errcode + ",errmsg = " + convertValue.get("errmsg"));
        }
        return convertValue;
    }

    /**
     * 根据 code 获取小程序 openid
     *
     * @param code
     * @param appId
     * @param appSecret
     * @return
     */
    public static JSONObject openId(String code, String appId, String appSecret) {

        if (StringUtils.isBlank(code)) {
            throw new ActionException("code不能为空");
        }
        String WX_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
        String requestUrl = WX_URL.replace("APPID", appId).replace("SECRET", appSecret).replace("JSCODE", code);
        String returnValue = HttpUtil.GET(requestUrl);
        // 将得到的字符串转换为json
        JSONObject convertValue = (JSONObject) JSON.parse(returnValue);
        String openId = (String) convertValue.get("openid");
        if (StringUtils.isBlank(openId)) {
            throw new ActionException("获取openid出错，errcode = " + convertValue.get("errcode"));
        }
        return convertValue;
    }

    /**
     * 根据formid ,通过templateDatas发送消息
     * 参数说明参考 https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#发送模板消息
     *
     * @param accessToken   小程序token
     * @param touser        消息推送对象的 openid
     * @param templateId    模板 template_id
     * @param page          跳转小程序页面可带参数
     * @param formId        表单id
     * @param templateDatas 自定义模板数据
     * @return
     */
    public static String sendMessageByTemplateDatas(String accessToken, String touser, String templateId, String page, String formId, List<WxMpTemplateData> templateDatas) {
        String WX_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN";
        String requestUrl = WX_URL.replace("ACCESS_TOKEN", accessToken);
        WxMpTemplateMessage templateMessage = new WxMpTemplateMessage(touser, templateId, page, formId, templateDatas);
        String json = templateMessage.toJson();
        String responseContent = HttpUtil.httpsRequest(requestUrl, "POST", json);
        JSONObject convertValue = (JSONObject) JSON.parse(responseContent);
        String errcode = String.valueOf(convertValue.get("errcode"));
        if (!Validator.equals("0", errcode)) {
            log.warn("发送消息失败，errcode = " + errcode);
        }
        return errcode;
    }

    /**
     * 根据formid ,通过根据messageType生成的params发送消息
     * 参数说明参考 https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#发送模板消息
     *
     * @param accessToken 小程序token
     * @param toUser      消息推送对象的 openid
     * @param templateId  模板 template_id
     * @param page        跳转小程序页面可带参数
     * @param formId      表单id
     * @param params      根据messageType生成的params
     * @return
     */
    public static String sendMessageByParams(String accessToken, String toUser, String templateId, String page, String formId, Map<String, String> params) {
        String WX_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN";
        String requestUrl = WX_URL.replace("ACCESS_TOKEN", accessToken);
        WxMpTemplateMessage templateMessage = new WxMpTemplateMessage(toUser, templateId, page, formId, params);
        String json = templateMessage.paramToJson();
        String responseContent = HttpUtil.httpsRequest(requestUrl, "POST", json);
        JSONObject convertValue = (JSONObject) JSON.parse(responseContent);
        String errcode = String.valueOf(convertValue.get("errcode"));
        if (!Validator.equals("0", errcode)) {
            log.warn("发送消息失败，errcode = " + errcode);
        }
        return errcode;
    }



    /**
     * 统一下单
     * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     * 签名算法
     * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
     *
     * @param appid          小程序ID
     * @param mchId          商户号
     * @param nonceStr       随机字符串
     * @param body           商品描述
     * @param attach         附加数据
     * @param outTradeNo     商户订单号
     * @param totalFee       标价金额
     * @param spbillCreateIp 终端IP
     * @param notifyUrl      通知地址
     * @param tradeType      交易类型 小程序取值如下：JSAPI
     * @param openId         用户标识 trade_type=JSAPI，此参数必传
     * @param key            商户密钥
     * @return
     */
    public static Map<String, String> wechatPay(String appid, String mchId, String nonceStr, String body, String attach,
                                                String outTradeNo, String totalFee, String spbillCreateIp, String notifyUrl,
                                                String key, String tradeType, String openId) {

        // 原单位为元，需转换为分
        totalFee = WechatPayUtil.getMoney(totalFee);
        // 后面需要键值对的形式，所以先装入map
        Map<String, String> sParaTemp = new HashMap(16);
        sParaTemp.put("appid", appid);
        sParaTemp.put("attach", attach);
        sParaTemp.put("body", body);
        sParaTemp.put("mch_id", mchId);
        sParaTemp.put("nonce_str", nonceStr);
        sParaTemp.put("notify_url", notifyUrl);
        sParaTemp.put("openid", openId);
        sParaTemp.put("out_trade_no", outTradeNo);
        sParaTemp.put("spbill_create_ip", spbillCreateIp);
        sParaTemp.put("total_fee", totalFee);
        sParaTemp.put("trade_type", tradeType);

        // 去掉空值跟签名参数(空值不参与签名，所以需要去掉)
        Map<String, String> map = WechatPayUtil.paraFilter(sParaTemp);
        // 对参数按照key=value的格式，并按照参数名ASCII字典序排序
        String mapStr = WechatPayUtil.createLinkString(map);

        // MD5运算生成签名
        String sign = WechatPayUtil.sign(mapStr, key, "utf-8").toUpperCase();
        sParaTemp.put("sign", sign);
        // 将map转化为下xml
        String xml = WechatPayUtil.mapToXml(sParaTemp);
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String result = WechatPayUtil.httpRequest(url, "POST", xml);
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        //得到预支付id
        String prepay_id = "";
        try {
            prepay_id = WechatPayUtil.getPayNo(result);
        } catch (DocumentException e) {
            log.warn("调用微信统一下单接口解析prepay_id异常", e);
        }
        String packages = "prepay_id=" + prepay_id;
        // 新的随机字符串
        String newNonceStr = WechatPayUtil.getNonceStr();
        // 拼接签名需要的参数
        String mapStr1 = "appId=" + appid + "&nonceStr=" + newNonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
        // 再次签名
        String paySign = WechatPayUtil.sign(mapStr1, key, "utf-8").toUpperCase();
        // 前端所需各项参数
        Map<String, String> params = new HashMap(16);
        params.put("appId", appid);
        params.put("timeStamp", timeStamp);
        params.put("nonceStr", newNonceStr);
        params.put("package", packages);
        params.put("signType", "MD5");
        params.put("paySign", paySign);
        return params;
    }

    /**
     * 微信支付回调
     *
     * @param request
     * @return
     */
    public static Map<String, String> callBack(HttpServletRequest request) {
        try {
            BufferedReader reader;
            reader = request.getReader();
            String line;
            String xmlString;
            StringBuffer inputString = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                inputString.append(line);
            }
            // 接收到的数据
            xmlString = inputString.toString();
            request.getReader().close();
            // xml字符串装化为map
            return WechatPayUtil.xmlToMap(xmlString);
        } catch (Exception e) {
            log.warn("微信回调数据解析异常", e);
        }
        return null;
    }
}
